#pragma once
#include <vector>
#include <map>
#include <cassert>
#include <iostream>
#include <string>

#include <hfsme/detail/HStaticTreeStructure.h>
#include <hfsme/detail/HStateHierarchyIterator.h>
#include <hfsme/detail/CastingPolicy.h>
#include <hfsme/HStateMachine.h>

namespace HFSME {

class HEventBase;



//TODO STATEALIAS - map between enum and state to allow transitions betweeen .cpp's

//struct StateMachine
//{
//	void init(void)
//	{
//		//instatiate all substates
//	}
//	void on_A()
//	{
//		tran<CHLD123>();
//	}
//
//};
//struct StateA:StateBase<StateA,StateMachine>
//{
//
//};
//static void register_states()
//{
//	ss.register_state<StateA,enumA>();
//	ss.register_state<StateA,enumA>();
//	ss.register_state<StateA,enumA>();
//	ss.register_state<StateA,enumA>();
//	ss.register_state<StateA,enumA>();
//};
//ADD_SPECIFIC_INITIALIZATION(StateMachine,&register_states);
//

template<typename T>
struct TypeToType
{
	typedef T originalType;
};



class HStateBase
{
private:
	typedef void (*trampoline)(HStateBase * state,HEventBase*evt);
	std::map<int,trampoline> localEvtMap;



	virtual detail::HStaticTreeStructure& getTheDerivedTreeStructure()=0;
	virtual void load_state_hierarchy() = 0;
public:
	friend class HStateMachineBase;
	virtual ~HStateBase()
	{

	}
	template<typename DestState>
	void tran()
	{
		//get the state index

	}
	void tran(StateId id)
	{

	}
	virtual void local_entry(void)
	{

	}
	void local_dispatch(int eventId,HEventBase* evt)
	{
		auto tramp=localEvtMap[eventId];
		tramp(this,evt);
	}
	template<typename ConcreteState,typename ConcreteEvent,void (ConcreteState::*member) (const ConcreteEvent &)>
	void register_handler_imp()
	{
		if(ConcreteEvent::theEventTypeId==-1)
		{
			ConcreteEvent::theEventTypeId=HEventBase::maxEventId++;
		}
		struct local{
			static void trampoline(HStateBase *state,HEventBase* evt)
			{
				ConcreteState * cstate=static_cast<ConcreteState*>(state);
				(cstate->*member)(*static_cast<ConcreteEvent*>(evt));
			}
		};
		localEvtMap.insert(std::make_pair(ConcreteEvent::theEventTypeId,&local::trampoline));
	}
	virtual StateId 		getStateId(void) const = 0;
	virtual std::string		getName(void) const = 0;
};


template
<
typename ConcreteState,
typename Parent,
template <class,class> class CastingPolicy = detail::Safe_caster
>
class HState:public HStateBase
{
public:
	struct hierarchy : Parent::hierarchy {
		ConcreteState*	myself;
		enum {depth = Parent::hierarchy::depth +1 };
		hierarchy()
			:myself(0)
		{}
		void initialize(detail::HStateHierarchyIterator & iter)
		{
			myself=CastingPolicy<ConcreteState*,detail::HStateHierarchyIterator &>::cast(iter.getState());
			iter.stepDown();
			//recursive initialization of the hierarchy of pointers
			Parent::hierarchy::initialize(iter);
		}
	};
private:
	hierarchy	theParentPointers;
	virtual void load_state_hierarchy()
	{

	}
public:
	typedef Parent							parent_state;
	typedef typename Parent::state_machine 	state_machine;
	HState()
	{}


	//============== Static tree structure management =================
	virtual detail::HStaticTreeStructure& getTheDerivedTreeStructure()
	{
		return getTheTreeStructure();
	}
	static detail::HStaticTreeStructure& getTheTreeStructure()
	{
		//assure that this variable is initialized before it's accessed
		//putting in into a static function
		static detail::HStaticTreeStructure theTreeStructure;
		return theTreeStructure;
	}
	template <typename SubState,bool isInitial=false>
	static int register_substate(const char * name)
	{
		assert(SubState::getOrSetStateId()==-1 && "Double Id Initialization");

		//set the state id
		SubState::getOrSetStateId()=state_machine::getStateIdCounter()++;

		//set the name
		SubState::getOrSetName()=std::string(name);

		getTheTreeStructure().register_substate<SubState,isInitial>();
		return 1;
	}

	template <typename ConcreteEvent,void (ConcreteState::*member) (const ConcreteEvent &)>
	void register_handler()
	{
		//forward to the base impl. used only to reduce the typing
		HStateBase::register_handler_imp<ConcreteState,ConcreteEvent,member>();
	}

	template<typename T>
	T& get_parent(void)
	{
		typedef typename T::hierarchy parent_hierarchy;
		assert(static_cast<parent_hierarchy*>(&theParentPointers)->myself);
		return *static_cast<parent_hierarchy*>(&theParentPointers)->myself;
	}
	virtual StateId getStateId(void) const
	{
		return getOrSetStateId();
	}
	virtual std::string	getName(void) const
	{
		return getOrSetName();
	}
	static StateId & getOrSetStateId(void)
	{
		static StateId theStateId = -1;
		return theStateId;
	}
	static std::string & getOrSetName(void)
	{
		static std::string theName;
		return theName;
	}
protected:
	state_machine & get_state_machine(void)
	{
		assert(static_cast<typename Parent::state_machine::hierarchy*>(&theParentPointers)->myself);
		return *static_cast<typename Parent::state_machine::hierarchy*>(&theParentPointers)->myself;
	}

//	CREATE_CLASS_HAS_MEMBER_TRAIT(ConcreteState,void,void,entry)
//	virtual void local_entry(void)
//	{
//		std::cout <<"R "<< class_has_member_entry::value << std::endl;
//		call_entry(detail::Int2Type<class_has_member_entry::value>());
//	}
//	void call_entry(detail::Int2Type<0>)
//	{
//		std::cout << "NO entry" << std::endl;
//	}
//	void call_entry(detail::Int2Type<1>)
//	{
//		std::cout << "Has entry" << std::endl;
//		static_cast<ConcreteState&>(*this).entry();
//	}

};


//avoid static to create a link error if a state is registered more than once
#define REGISTER_STATE(State)	const int  __attribute__((unused)) __dummy##State=State::parent_state::register_substate<State>(#State);

//template<typename T,typename Parent> const int HState<T,Parent>::registered(Parent::register_substate(TypeToType<T>()));


}  // namespace hfsme
